package xpn.platform.common.repository;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

public class XpnRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
		implements XpnRepository<T, ID> {

	private final int MAX_RESULT_SIZE = 1000; // 最大结果数量

	private final EntityManager entityManager;

	public XpnRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
		super(entityInformation, entityManager);
		this.entityManager = entityManager;
	}

	@Override
	public List<T> filterByJpql(String jpql) {
		Query query = this.entityManager.createQuery(jpql);

		@SuppressWarnings("unchecked")
		List<T> result = query.getResultList();
		return result;
	}

	@Override
	public Page<T> filterByJpql(String jpql, Pageable pageable, Sort sort) {
		// 1 查询总条目数
		long count = getQueryCount(jpql, pageable);

		// 2 执行查询并返回结果
		// 构造排序规则
		jpql += constructSortJpql(sort);

		Query query = this.entityManager.createQuery(jpql);

		// 设置分页参数
		Pageable pagingParam = setPageable(query, pageable);

		@SuppressWarnings("unchecked")
		List<T> result = query.getResultList();
		return new PageImpl<T>(result, pagingParam,
				pageable == null || pageable.getPageSize() >= MAX_RESULT_SIZE ? result.size() : count);
	}

	@Override
	public Object countByJpql(String jpql) {
		// 1 查询总条目数
		Long count = getQueryCount(jpql);
        return count;
	}

	@Override
	public Object executeJpql(String jpql) {
		Query query = this.entityManager.createQuery(jpql);
		return query.getResultList();
	}	// 获得查询的结果记录个数


	@Override
	public List<T> filterBySql(String sql, @SuppressWarnings("rawtypes") Class resultClass) {
		if (resultClass == null) {
			resultClass = this.getDomainClass();
		}
		Query query = this.entityManager.createNativeQuery(sql, resultClass);

		@SuppressWarnings("unchecked")
		List<T> result = query.getResultList();
		return result;
	}

	@Override
	public Page<T> filterBySql(String sql, @SuppressWarnings("rawtypes") Class resultClass, Pageable pageable,
			Sort sort) {
		// 1 查询总条目数
		long count = getQueryCount(sql, pageable);

		// 2 执行查询并返回结果
		// 构造排序规则
		sql += constructSortJpql(sort);

		if (resultClass == null) {
			resultClass = this.getDomainClass();
		}
		Query query = this.entityManager.createNativeQuery(sql, resultClass);

		// 设置分页参数
		Pageable pagingParam = setPageable(query, pageable);

		@SuppressWarnings("unchecked")
		List<T> result = query.getResultList();
		return new PageImpl<T>(result, pagingParam, pageable == null ? result.size() : count);
	}

	@Override
	public T findOneByJpql(String jpql, ID id) {
		// 1 构造查询JPQL;
		jpql += " where o.id=" + id;
		Query query = this.entityManager.createQuery(jpql);

		// 3 查询并返回结果
		@SuppressWarnings("unchecked")
		List<T> result = query.getResultList();
		if (result.size() > 0) {
			return result.get(0);
		} else {
			return null;
		}
	}

	@Override
	public T findOneByJpql(String jpql) {
		Query query = this.entityManager.createQuery(jpql);

		// 3 查询并返回结果
		@SuppressWarnings("unchecked")
		List<T> result = query.getResultList();
		if (result.size() > 0) {
			return result.get(0);
		} else {
			return null;
		}
	}

	// 获得查询的结果记录个数
	private long getQueryCount(String jpql, Pageable pageable) {
		if (pageable == null) {
			return 0;
		}

		if (pageable.getPageSize() == 0 || pageable.getPageSize() >= MAX_RESULT_SIZE) {
			return 0;
		}

		String countJpql = constructCountQueryJpql(jpql);
		Query countQuery = entityManager.createQuery(countJpql, Long.class);

		return (long) countQuery.getSingleResult();
	}

	// 获得查询的结果记录个数
	private long getQueryCount(String jpql) {
		Query countQuery = entityManager.createQuery(jpql, Long.class);
		return (long) countQuery.getSingleResult();
	}



	// 根据jpql构造计算总数的jpql
	private String constructCountQueryJpql(String jpql) {
		jpql = jpql.trim();

		// 计算各个子句的位置
		String tmpJpql = jpql.toLowerCase();
		String fromKey = "from";
		int fromPos = tmpJpql.lastIndexOf(fromKey);
		String whereKey = "where";
		int wherePos = tmpJpql.lastIndexOf(whereKey);

		// 获取from
		String from = jpql.substring(fromPos, wherePos > fromPos ? wherePos : jpql.length());

		// 获取dto
		String[] fromArr = from.split(" ");
		String dto = fromArr[2];

		// 获取where
		String where = "";
		if (wherePos > 0) {
			where = jpql.substring(wherePos);
		}

		// 构造count子句
		String selectCount = "select count(" + dto + ")";
		if (tmpJpql.indexOf(" distinct ") > 0 || tmpJpql.indexOf(" join ") > 0) {
			selectCount = "select count(distinct " + dto + ")";
		}

		// 返回完整的计算总数jpql
		from = from.replaceAll(" FETCH", "");
		from = from.replaceAll(" fetch", "");
		return selectCount + " " + from + " " + where;
	}

	// 根据传入的sort参数，构造排序jpql
	private String constructSortJpql(Sort sort) {
		String sortJpql = "";
		if (sort != null) {
			Iterator<Order> iterator = sort.iterator();
			while (iterator.hasNext()) {
				Order order = iterator.next();
				sortJpql += ("".equals(sortJpql) ? "" : ",") + order.getProperty() + " " + order.getDirection();
			}
			sortJpql = " order by " + sortJpql;
		}
		return sortJpql;
	}

	// 设置分页参数
	private Pageable setPageable(Query query, Pageable pageable) {
		if (pageable == null) {
			List<Order> orders = new ArrayList<>();
			orders.add(new Order(Sort.Direction.ASC, "id"));
			pageable = PageRequest.of(0, 1, Sort.by(orders));
		}

		/*
		 * 郑波2017-11-26修改：页尺寸可以超过1000： if (pageable.getPageSize() >= MAX_RESULT_SIZE) {
		 * pageable = new PageRequest(0, MAX_RESULT_SIZE); }
		 */

		query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
		query.setMaxResults(pageable.getPageSize());

		return pageable;
	}

	@Transactional(readOnly = false)
	@Override
	public List<T> batchsave(BatchSaveEntity<T> bachsaveEntiry) {
		List<T> list=new ArrayList<T>();
		if(bachsaveEntiry!=null){
			Set<T> inserted=bachsaveEntiry.getInserted();
			if(inserted!=null){
				for(T e:inserted){
					e=entityManager.merge(e);
					list.add(e);
				}
			}

			Set<T> deleted=bachsaveEntiry.getDeleted();
			if(deleted!=null){
				for(T e:deleted){
					e=entityManager.merge(e);
					entityManager.remove(e);
				}
			}
			Set<T> updated=bachsaveEntiry.getUpdated();
			if(updated!=null){
				for(T e:updated){
					e=entityManager.merge(e);
					list.add(e);
				}
			}
			entityManager.flush();
		}
		return list;
	}
}
